<template>
  <PageWrapper>
    <!-- 笔记 -->
    <CollapseContainer class="mt-5" title="props">
      <markdown-viewer :value="getDoc()" />
    </CollapseContainer>

    <CollapseContainer class="mt-5" title="provider&inject 用法示例">
      <Grand />

      <template #action>
        <Icon icon="ant-design:code-outlined" class="mr-1" :size="26" @click="openSource('源码示例', getBasic())" />
      </template>
    </CollapseContainer>

    <!-- 源码组件 -->
    <SourceDrawer ref="sourceDrawer" />
  </PageWrapper>
</template>
<script lang="ts" setup>
  import { CollapseContainer } from '@/components/Container';
  import { MarkdownViewer } from '@/components/Markdown';
  import { PageWrapper } from '@/components/Page';
  import SourceDrawer from '@/learn/SourceDrawer.vue';
  import { ref } from 'vue';
  import { Icon } from '@/components/Icon';
  import Grand from './Grand.vue';

  // 定义子组件引用
  let sourceDrawer = ref<any>(null);
  function openSource(title: string, content: string) {
    sourceDrawer.value.open({ title, content, width: '60%' });
  }
</script>

<script lang="ts">
  function getDoc() {
    return `
* provider&inject 用于有点儿类似于组件上下文context 的意思，像 http 请求重点 RequestContext 一样。
* 上层组件保存的元素，下层组件都可以使用。也有点儿事件广播的韵味儿，下层组件可以重复消费，不像 $attrs，只能被消费一次
* 用法步骤:
  * 上层组件，通过调用 provider 将本组件元素保存到上下文中。上下文可以理解为一个 Map结构
  * 下层组件，通过调用 inject 获取上游设置的元素，元素可以是变量、方法等。
* 注意点：
  * 如果设置响应式对象，一定不要是基本类型，因为基本类型会失去响应式
  * 响应对象一定要设置成 对象或数组，这样才能保持响应式    
`;
  }

  function getBasic() {
    return `
### 祖-组件
\`\`\`html
<template>
  <div class="mt-5 bg-green-300 p-5" title="父组件" :can-expan="false">
    <h3>祖-组件</h3>
    <ul>
      <li>普通对象 name:{{ name }}</li>
      <li>响应对象 car:{{ car }}</li>
      <li>接收子回调数据:{{ data }}</li>
    </ul>

    <Father />
  </div>
</template>

\<script lang="ts" setup\>
  import Father from './Father.vue';
  import { ref, provide } from 'vue';

  // 普通对象: 不具有响应式
  let name: string = 'hello';

  // 响应对象: 孙组件修改同步修改
  let car = ref({
    brand: '奔驰',
    price: 20,
  });

  // 存储孙组件回调数据
  let data = ref();

  // 接受孙组件回调
  function setData(callBackData: string) {
    data.value = callBackData;
  }

  // 设置属性到组件上下文，有方法、有变量
  provide('name', name);
  provide('car', car);
  provide('setData', setData);
\</script\>
\`\`\`

### 父-组件
\`\`\`html
<template>
  <div class="mt-5 bg-gray-300 p-5" title="父组件" :can-expan="false">
    <h3>父-组件</h3>
    <ul>
      <li>inject 接收到的参数, 响应对象 car:{{ car }}</li>
      <li>inject 接收到的参数, 响应对象 car:{{ car }}</li>
      <li>解构品牌 brand: {{ brand }}<Button @click="changeBrand">修改</Button>, 祖同步修改</li>
    </ul>

    <Child />
  </div>
</template>

\<script lang="ts" setup\>
  import { Button } from '@/components/Button';

  import Child from './Child.vue';
  import { ref, toRefs, inject } from 'vue';

  let car = inject('car', ref());

  // 解构品牌
  let { brand } = toRefs(car.value);

  // 修改品牌，则子组件、父组件全部同步修改
  function changeBrand() {
    brand.value += '~';
  }
\</script\>
\`\`\`


### 孙-组件
\`\`\`html
<template>
  <div class="mt-5 bg-blue-200 p-5" title="子组件" :can-expan="false">
    <h3>孙-组件</h3>
    <ul>
      <li>inject 接收到的参数, 普通对象 name:{{ name }} <Button @click="changeName">延迟修改</Button></li>
      <li>inject 接收到的参数, 响应对象 car:{{ car }}</li>
      <li>解构品牌 brand: {{ brand }}<Button @click="changeBrand">修改</Button>, 祖同步修改</li>
      <li>
        <input v-model="inputData" />
        <Button @click="sendData">向祖组件发送数据</Button>
      </li>
    </ul>
  </div>
</template>

\<script lang="ts" setup\>
  import { Button } from '@/components/Button';
  import { ref, toRefs, inject } from 'vue';

  let name = inject('name');
  let car = inject('car', ref());
  let setData = inject('setData', (data: string): void => {
    console.log(data);
  });

  // 解构品牌
  let { brand } = toRefs(car.value);

  // 输入框的值
  let inputData = ref();

  // 修改品牌，则子组件、父组件全部同步修改
  function changeBrand() {
    brand.value += '~';
  }

  // 修改品牌，则子组件、父组件全部同步修改
  function changeName() {
    // 直接改 name 页面不会直接变更，等点 changeBrand 触发了 brand 变化后，才会重新刷新
    name += '~';
  }

  // 向父组件
  function sendData() {
    setData(inputData.value);
  }
\</script\>
\`\`\`

`;
  }
</script>

<style lang="less" scoped>
  button {
    margin-left: 5px;
  }
</style>
